package tables

import (
	"fmt"
	"net"
	"ospf/common"
	"sync"

	"github.com/vishvananda/netlink"
)

type Route struct {
	NextHop common.ID
	Cost    int
}

type RoutingTable struct {
	Routes map[common.ID]Route
	mu     sync.Mutex
}

// AddRouteTable 向哈希表添加路由信息
func (rt *RoutingTable) AddRouteTable(ip common.ID, route Route) error {
	// 为该路由器的所有ip获取下一跳的地址
	gw := net.ParseIP(route.NextHop.String())
	if gw == nil {
		return common.ErrUpdateRouting("invalid next hop address.")
	}
	rt.mu.Lock()
	defer rt.mu.Unlock()
	update := false
	// 如果路由表中没有出现过该ip或者ip的下一跳有变化，则需要更新路由表
	if _, exist := rt.Routes[ip]; !exist {
		update = true
	} else if rt.Routes[ip] != route {
		update = true
	}
	if update {
		rt.Routes[ip] = route
	}
	return nil
}

// DelRouteTable 删除哈希表中路由相关信息
func (rt *RoutingTable) DelRouteTable(ip common.ID) error {
	rt.mu.Lock()
	defer rt.mu.Unlock()
	delete(rt.Routes, ip)
	return nil
}

// DelRoute 检查路由表中是否存在相关ip的路由信息，如果存在，则进行删除
func (rt *RoutingTable) DelRoute(ip common.ID, needsLock bool) error {
	if needsLock {
		rt.mu.Lock()
		defer rt.mu.Unlock()
	}
	if route, ok := rt.Routes[ip]; ok {
		// 读取路由表项相关数据并删除
		ipNet, err := netlink.ParseIPNet(ip.StringCIDR())
		gw := net.ParseIP(route.NextHop.String())
		if err != nil {
			return common.ErrUpdateRouting("fail to del route, gateway ip not valid.\n" + err.Error())
		}
		routing := &netlink.Route{
			Dst: ipNet,
			Gw:  gw,
		}
		err = netlink.RouteDel(routing)
		if err != nil {
			return common.ErrUpdateRouting("fail to del route, routing table deletion failed.\n" + err.Error())
		}
		delete(rt.Routes, ip)
	}
	return nil
}

func (rt *RoutingTable) AddRoute(ip common.ID, route Route, needsLock bool) error {
	// 为该路由器的所有ip获取下一跳的地址
	gw := net.ParseIP(route.NextHop.String())
	if gw == nil {
		return common.ErrUpdateRouting("invalid next hop address.")
	}
	if needsLock {
		rt.mu.Lock()
		defer rt.mu.Unlock()
	}
	update := false
	// 如果路由表中没有出现过该ip或者ip的下一跳有变化，则需要更新路由表
	if _, exist := rt.Routes[ip]; !exist {
		update = true
	} else if rt.Routes[ip] != route {
		update = true
	}
	dst, err := netlink.ParseIPNet(ip.StringCIDR())
	if err != nil {
		return common.ErrUpdateRouting(err.Error())
	}
	if update {
		err = rt.DelRoute(ip, false)
		if err != nil {
			return err
		}
		rt.Routes[ip] = route
		err = netlink.RouteAdd(&netlink.Route{
			Dst: dst,
			Gw:  gw,
		})
		if err != nil {
			return err
		}
	}
	return nil
}

// AddMultipleRoute 为对应ip添加路由，一次性接收多个 ip
func (rt *RoutingTable) AddMultipleRoute(ips []common.ID, route Route) error {
	fmt.Println("add multiple route: ", ips, route)
	// 为该路由器的所有ip获取下一跳的地址
	gw := net.ParseIP(route.NextHop.String())
	if gw == nil {
		return common.ErrUpdateRouting("invalid next hop address.")
	}
	rt.mu.Lock()
	defer rt.mu.Unlock()
	for _, ip := range ips {
		err := rt.AddRoute(ip, route, false)
		if err != nil {
			return err
		}
	}
	return nil
}

func NewRoutingTable() *RoutingTable {
	return &RoutingTable{
		Routes: make(map[common.ID]Route),
	}
}
